package com.googlecode.ddplan.dwr.domain;

import com.googlecode.ddplan.PDDLAction;
import com.googlecode.ddplan.PDDLName;
import com.googlecode.ddplan.PDDLState;
import com.googlecode.ddplan.PDDLType;

@PDDLType
public class Crane {
	@PDDLName
	private String name;

	private Box box;

	public Crane(String name) {
		this.name = name;
	}

	@PDDLState
	public boolean belongsTo(Location location) {
		return location.hasCrane(this);
	}

	@PDDLState
	public boolean empty() {
		return this.box == null;
	}

	@PDDLState
	public boolean holding(Box box) {
		return this.box.equals(box);
	}

	@PDDLAction(precond = { "belongsTo($1)", "holding($2)", "$3.at($1)", "$3.unloaded" }, effects = { "empty", "!holding($2)",
			"$3.loaded($2)", "!$3.unloaded" })
	public void load(Location location, Box box, Robot robot) {
		if (!canLoad(location, box, robot)) {
			throw new IllegalArgumentException("Crane cannot load the robot!");
		}
		this.box = null;
		robot.take(box);
	}

	private boolean canLoad(Location location, Box box, Robot robot) {
		return belongsTo(location) && holding(box) && robot.at(location) && robot.unloaded();
	}

	@PDDLAction(precond = { "belongsTo($1)", "empty", "$3.at($1)", "$3.loaded($2)" }, effects = { "!empty", "holding($2)", "$3.unloaded",
			"!$3.loaded($2)" })
	public void unload(Location location, Box box, Robot robot) {
		if (!canUnload(location, box, robot)) {
			throw new IllegalArgumentException("Crane cannot unload the robot!");
		}
		this.box = box;
		robot.release();
	}

	private boolean canUnload(Location location, Box box, Robot robot) {
		return belongsTo(location) && empty() && robot.at(location);
	}

	@PDDLAction(precond = { "belongsTo($1)", "$4.attached($1)", "holding($2)", "$3.top($4)" }, effects = { "empty", "!holding($2)",
			"$2.in($4)", "$2.top($4)", "$2.on($3)", "!$3.top($4)" })
	public void put(Location location, Box box, Box box2, Pile pile) {
		if (!canPut(location, box, box2, pile)) {
			throw new IllegalArgumentException("Crane cannot load the pile!");
		}
		box2.put(box);
		this.box = null;
	}

	private boolean canPut(Location location, Box box, Box box2, Pile pile) {
		return belongsTo(location) && pile.attached(location) && holding(box) && box2.top(pile);
	}

	@PDDLAction(precond = { "belongsTo($1)", "$4.attached($1)", "empty", "$2.top($4)", "$2.on($3)" }, effects = { "!empty", "holding($2)",
			"!$2.in($4)", "!$2.top($4)", "!$2.on($3)", "$3.top($4)" })
	public void take(Location location, Box box, Box box2, Pile pile) {
		if (!canTake(location, box, box2, pile)) {
			throw new IllegalArgumentException("Crane cannot unload the pile!");
		}
		box2.free();
		this.box = box;
	}

	private boolean canTake(Location location, Box box, Box box2, Pile pile) {
		return belongsTo(location) && pile.attached(location) && empty() && box.top(pile) && box.on(box2);
	}
}
